#pragma once
#include <iostream>
#include <cerrno>
#include <memory>
#include "Connection.hpp"
#include "RequestResponse/Protocol.hpp"
#include "RequestResponse/ServerManager.hpp"

extern std::unique_ptr<ServerManager> server_manager;
extern std::unordered_map<std::string, Room *> rooms;
extern std::unordered_map<int, std::string> fd_to_room_number;

void Reader(Connection *conn);
void Writer(Connection *conn);
void Exceptor(Connection *conn);

void ServerHandler(Connection *conn)
{
    std::string &inbuffer = conn->GetInbuffer();
    std::string msg;
    // 解析获得一个完整的独立的请求报文
    // std::cout << "inbuffer: " << inbuffer << std::endl;
    while (Unpackage(inbuffer, &msg))
    {
        // std::cout << "Package: " << msg << std::endl;
        // inbuffer.erase(0, msg.size());

        // std::cout << "Request: " << msg << std::endl;

        bool ok = true;
        // 对获取到的请求报文进行处理获得响应报文
        std::string result = server_manager->GetRequsestAndResponse(msg, &ok, conn);
        if (ok)
        {
            // std::cout << "Response: " << result << std::endl;
            // 封装响应报文
            Package(result, &result);
            // 放到发送缓冲区中
            conn->GetOutbuffer() += result;
        }
        else
        {
            // std::cout << "Result: " << result << std::endl;
            break;
        }
        // std::cout << "Result: " << result << std::endl;
    }

    // 如果上面的循环中成功处理数据，则发送缓冲区中有数据，此时我们直接发送数据
    if (!conn->OutEmpty())
    {
        // std::cout << "###########" << std::endl;
        Writer(conn);
    }
}

void Accept(Connection *conn)
{
    // std::cout << "新连接到来" << std::endl;

    errno = 0;
    while (true)
    {
        struct sockaddr_in clientaddr;
        socklen_t len = sizeof(clientaddr);
        int newsock = accept(conn->GetFd(), (struct sockaddr *)(&clientaddr), &len);
        if (newsock > 0)
        {
            // 新连接到来
            // 将新到来的文件描述符设置为非阻塞
            SetNonBlock(newsock);
            // 创建普通连接对象
            Connection *newconn = Factory::BuildNormalConn(newsock, EPOLLIN | EPOLLET, Reader, Writer, Exceptor, conn->GetReactor());
            // 将创建好的连接对象放入Reactor中
            conn->GetReactor()->AddConnection(newconn, EPOLLIN | EPOLLET);
        }
        else
        {
            if (newsock == 0)
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN)
                    break;
            }
            else
            {
                break;
            }
        }
    }
}

void Reader(Connection *conn)
{
    // std::cout << "普通文件描述符读事件就绪" << std::endl;
    errno = 0;
    //

    // std::cout << "fd: " << conn->GetFd() << std::endl;
    while (true)
    {
        char buffer[1024];
        ssize_t n = recv(conn->GetFd(), buffer, sizeof(buffer) - 1, 0);

        // std::cout << "n: " << n << std::endl;
        if (n > 0)
        {
            buffer[n] = 0;
            conn->GetInbuffer() += buffer;
            // std::cout << "inbuffer: " << conn->GetInbuffer() << std::endl;
            // std::cout << conn->GetInbuffer() << std::endl;
        }
        else if (n == 0)
        {
            // std::cout << "conn->GetInbuffer(): " << conn->GetInbuffer() << std::endl;
            if (!conn->GetInbuffer().empty())
            {
                ServerHandler(conn);
            }
            // std::cout << "对端关闭连接" << std::endl;
            Exceptor(conn);
            return;
        }
        else
        {
            if (errno == EAGAIN)
                break;
            else if (errno == EINTR)
                continue;
            else
            {

                // std::cout << "普通文件描述符异常事件就绪" << std::endl;
                Exceptor(conn);
                return;
            }
        }
    }

    // 当读取完数据之后，就开始处理数据
    ServerHandler(conn);
}

void Writer(Connection *conn)
{
    // std::cout << "普通文件描述符写事件就绪" << std::endl;
    errno = 0;
    //
    // std::string &outbuffer = conn->GetOutbuffer();
    while (true)
    {
        ssize_t n = send(conn->GetFd(), conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
        if (n >= 0)
        {
            // std::cout << "outbuffer: " << conn->_outbuffer << std::endl;
            conn->_outbuffer.erase(0, n);
            // std::cout << "outbufferoutbuffer: " << n << " " <<  conn->_outbuffer << std::endl;
            if (conn->OutEmpty())
                break;
        }
        else
        {
            if (errno == EAGAIN)
                break;
            else if (errno == EINTR)
                continue;
            else
            {
                Exceptor(conn);
                return;
            }
        }
    }

    // 从这里出来之后，有两种情况：
    // 一种是发送缓冲区不为空，这意味着，发送条件不就绪，这时候我们就需要epoll模型对该文件描述符的写事件关心
    if (!conn->OutEmpty())
    {
        conn->GetReactor()->ModConnection(conn, true, true);
    }
    // 另一种是发送缓冲区为空，这时候该文件描述符写事件仍被epoll模型关心，为了做到按需关心，我们应该从epoll模型中取消对该文件描述符的写事件关心
    else
    {
        conn->GetReactor()->ModConnection(conn, true, false);
    }
}

void Exceptor(Connection *conn)
{
    // 这里当玩家退出连接之后，我们首先判断他是房主还是成员
    // 如果是房主的话，我们需要给其他机器通知销毁房间的信息，并且在本地删除该房间中所有的文件描述符对房间号的映射，并且释放该房间，并且在管理房间的容器中删除该房间
    // 如果是成员的话，我们需要通知其他机器该成员退出房间，并且在本地删除该文件描述符对房间号的映射，并且在房间中删除该成员
    if (fd_to_room_number.find(conn->GetFd()) != fd_to_room_number.end())
    {
        std::vector<Player> &players = rooms[fd_to_room_number[conn->GetFd()]]->GetPlayers();

        if (players[0].GetConnection() == conn)
        {
            // 房主退出
            std::string msg;
            Json::Value root;
            root["server_type"] = DestoryRoom;
            Json::FastWriter writer;
            msg = writer.write(root);
            Package(msg, &msg);
            for (int i = 0; i < players.size(); i++)
            {
                if (players[i].Exist())
                {
                    Connection *conn_player = players[i].GetConnection();
                    // std::cout << "conn_player->GetOutbuffer() " << conn_player->GetOutbuffer() << std::endl;
                    if (conn != conn_player)
                    {
                        conn_player->GetOutbuffer() += msg;
                        // std::cout << "conn_player->GetOutbuffer() " << conn_player->GetOutbuffer() << std::endl;
                        conn_player->GetWriter()(conn_player);

                        conn_player->GetReactor()->DelConnection(conn_player);

                        // 关闭文件描述符
                        close(conn_player->GetFd());
                        // 销毁connection对象
                        delete conn_player;
                    }
                }
            }

            // 本地 删除
            std::string room_number;
            for (int i = 0; i < players.size(); i++)
            {
                if (players[i].Exist())
                {
                    Connection *conn_player = players[i].GetConnection();
                    room_number = fd_to_room_number[conn_player->GetFd()];
                    fd_to_room_number.erase(conn_player->GetFd());
                }
            }
            delete rooms[room_number];
            rooms.erase(room_number);
        }
        else
        {
            // 成员退出
            // 通知其他机器退出
            std::string msg;
            Json::Value root;
            root["server_type"] = QuitRoom;
            for (int i = 0; i < players.size(); i++)
            {
                if (players[i].Exist())
                {
                    Connection *conn_player = players[i].GetConnection();
                    if (conn == conn_player)
                    {
                        root["player_id"] = i;
                        break;
                    }
                }
            }
            Json::FastWriter writer;
            msg = writer.write(root);
            Package(msg, &msg);
            for (int i = 0; i < players.size(); i++)
            {
                if (players[i].Exist())
                {
                    Connection *conn_player = players[i].GetConnection();
                    // std::cout << "conn_player->GetOutbuffer() " << conn_player->GetOutbuffer() << std::endl;
                    if (conn != conn_player)
                    {
                        conn_player->GetOutbuffer() += msg;
                        // std::cout << "conn_player->GetOutbuffer() " << conn_player->GetOutbuffer() << std::endl;
                        conn_player->GetWriter()(conn_player);
                    }
                }
            }

            // 本地删除
            rooms[fd_to_room_number[conn->GetFd()]]->DelPlayer(conn);
            fd_to_room_number.erase(conn->GetFd());
        }
    }

    // 进入异常处理之后我们的处理方式只能是关闭文件描述符，而关闭文件描述符，需要做以下事情：
    // 从epoll模型中删除该文件描述符
    // 从connections中删除该连接对象
    conn->GetReactor()->DelConnection(conn);

    // 关闭文件描述符
    close(conn->GetFd());
    // 销毁connection对象
    delete conn;
}